﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using Green.FastInvoker;
using Green.Utility;

namespace Green.AgileMapper
{
    public class CollectionMappingRule : IMappingRule, IXmlConfigLoad
    {
        public string FromPoperty
        { get; private set; }

        public string ToPoperty
        { get; private set; }

        public bool IsConvertTo
        { get; private set; }

        public bool IsConvertFrom
        { get; private set; }


        /// <summary>
        /// 是否删除to中不存在于from中的item项
        /// </summary>
        public bool IsDeleteNotInFromItem
        { get; set; }

        private  Dictionary<Type,Type>  knowTypes=new Dictionary<Type, Type>();
        /// <summary>
        /// fromObjItem list和domainObjItem list相等的表达式（fromObjItem,domainObjItem）=>retun bool;
        /// </summary>
        public string EqualExpression
        {
            get;
            set;
        }
        /// <summary>
        /// 默认fromObjItem list和domainObjItem list相等的表达式（from,domainObjItem）=>retun bool;：建议优先
        /// </summary>
        public Func<object, object, bool> DefaultEqualExpression
        {
            get;
            set;
        }

        protected virtual bool GetEqualFunction(object from, object to)
        {
            if (!string.IsNullOrEmpty(EqualExpression))
            {
                var dict = new Dictionary<string, object>();
                dict.Add("fromObjItem", from);
                dict.Add("domainObjItem", to);
                return Green.Utility.DynamicExpression.Compute<bool>(dict, this.EqualExpression);
            }
            else if (DefaultEqualExpression != null)
            {
                return DefaultEqualExpression(from, to);
            }
            return false;

        }

        public static Predicate<Type> IsPrimitivePredicate = (t => t.IsPrimitive || t == typeof(string) || t.IsEnum || t.IsValueType || (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Nullable<>)));

        public CollectionMappingRule(string fromPoperty, string toPoperty, bool isConvertTo, bool isConvertFrom)
        {
            this.FromPoperty = fromPoperty;
            this.ToPoperty = toPoperty;
            this.IsConvertFrom = isConvertFrom;
            this.IsConvertTo = isConvertTo;
            IsDeleteNotInFromItem = true;
        }

        public CollectionMappingRule(string fromPoperty, string toPoperty)
            : this(fromPoperty, toPoperty, true, true)
        { }

        public CollectionMappingRule()
        {
        }

        #region IMappingRule 成员
        /// <summary>
        /// 根据 To得到From
        /// </summary>
        /// <param name="from"></param>
        /// <param name="to"></param>       
        public virtual void WarpFrom(object from, object to)
        {
            if (IsConvertFrom)
            {
                IList collection = PropertyAccessor.Get(to, this.ToPoperty) as IList;
                if (collection != null)
                {
                    var fromPropertyType = PropertyAccessor.GetPropertyType(from, this.FromPoperty).GetGenericArguments()[0];
                    var fromProperty = Activator.CreateInstance(typeof(List<>).MakeGenericType(fromPropertyType)) as IList;
                    foreach (var item in collection)
                    {
                        object fromObject = null;

                        if (IsPrimitivePredicate(fromPropertyType))
                        {
                            if (item.GetType() == fromPropertyType)
                            {
                                fromObject = item;
                            }
                            else
                            {
                                fromObject = item.ChangeType(fromPropertyType);
                            }
                        }
                        else if (fromPropertyType.IsClass)
                        {
                            var objectMapper = new DefaultObjectMapper(new AttributeMappingConfigurator());
                            fromObject = objectMapper.Warp(fromPropertyType, item);
                        }
                        fromProperty.Add(fromObject);
                    }
                    PropertyAccessor.Set(from, this.FromPoperty, fromProperty);
                }
            }
        }
        /// <summary>
        /// 根据From得到To
        /// </summary>
        /// <param name="from"></param>
        /// <param name="to"></param>
        public virtual void WarpTo(object from, object to)
        {
            if (IsConvertTo)
            {
                var toPropertyType = PropertyAccessor.GetPropertyType(to, this.ToPoperty).GetGenericArguments()[0];
                IList toproperty = PropertyAccessor.Get(to, this.ToPoperty) as IList;
                var fromProperty = PropertyAccessor.Get(from, this.FromPoperty) as IList;
                var frompropertyType = PropertyAccessor.GetPropertyType(from, this.FromPoperty).GetGenericArguments()[0];
                if (fromProperty != null)
                {
                    if (toproperty == null)
                    {
                        toproperty = Activator.CreateInstance(typeof(List<>).MakeGenericType(toPropertyType)) as IList;
                    }

                    //如果需要删除不存在于 from的list集合,先删除后修改
                    if (this.IsDeleteNotInFromItem)
                    {

                        for (int i = 0; i < toproperty.Count; i++)
                        {
                            var isFind = false;
                            var toItem = toproperty[i];
                            foreach (var fromItem in fromProperty)
                            {
                                if (GetEqualFunction(fromItem, toItem) || fromItem.Equals(toItem))
                                {
                                    isFind = true;
                                    break;
                                }
                            }
                            if (!isFind)
                            {
                                toproperty.RemoveAt(i);
                            }
                        }
                    }

                    foreach (var fromItem in fromProperty)
                    {
                        if (IsPrimitivePredicate(fromItem.GetType()))
                        {
                            if (!toproperty.Contains(fromItem))
                            {
                                if (fromItem.GetType() == toPropertyType)
                                    toproperty.Add(fromItem);
                                else
                                    toproperty.Add(fromItem.ChangeType(toPropertyType));
                            }
                        }
                        else if (toPropertyType.IsClass)
                        {
                            object toItemPoint = null;
                            foreach (var toItem in toproperty)
                            {

                                if (GetEqualFunction(fromItem, toItem) || toItem == fromItem)
                                {
                                    toItemPoint = toItem;
                                    break;
                                }
                            }
                            if (toItemPoint == null)
                            {
                                toItemPoint = Activator.CreateInstance(toPropertyType);
                                toproperty.Add(toItemPoint);
                            }
                            var objectMapper = new DefaultObjectMapper(new AttributeMappingConfigurator());
                            objectMapper.Warp(toPropertyType, fromItem, toItemPoint);
                        }

                    }


                    PropertyAccessor.Set(to, this.ToPoperty, toproperty);
                }
            }
        }

        #endregion

        #region IXmlConfigLoad 成员

        public void LoadXmlConfig(Dictionary<string, object> propertys)
        {
            this.FromPoperty = propertys.TryGetValue<string, string>("FromPoperty", string.Empty);
            this.ToPoperty = propertys.TryGetValue<string, string>("ToPoperty", string.Empty);
            this.IsConvertTo = propertys.TryGetValue<string, bool>("IsConvertTo", true);
            this.IsConvertFrom = propertys.TryGetValue("IsConvertFrom", true);
            this.IsDeleteNotInFromItem = propertys.TryGetValue("IsDeleteNotInFromItem", true);
            this.EqualExpression = propertys.TryGetValue<string, string>("EqualExpression", string.Empty);
            this.DefaultEqualExpression = propertys.ContainsKey("DefaultEqualExpression") ? (Func<object, object, bool>)propertys["DefaultEqualExpression"] : null;
        }

        #endregion
    }
}
